<?php

/**
 * Request 请求参数生成类
 * - 扩展Yaf请求类的功能：兼容接收JSON和XML的参数，格式化Header。
 * Class Request
 */
class Request
{
    /**
     * @var array $data 主数据源，原始参数
     */
    protected $data = array();

    /**
     * @var array $get 备用数据源 $_GET
     */
    protected $get = array();

    /**
     * @var array $post 备用数据源 $_POST
     */
    protected $post = array();

    /**
     * @var array $request 备用数据源 $_REQUEST
     */
    protected $request = array();

    /**
     * @var array $cookie 备用数据源 $_COOKIE
     */
    protected $cookie = array();

    /**
     * @var array $headers 备用数据源 请求头部信息
     */
    protected $headers;

    /**
     * - 如果需要定制已知的数据源（即已有数据成员），则可重载此方法，例
     *
     * ```
     * class MyRequest extend Request{
     *     public function __construct($data = NULL) {
     *         parent::__construct($data);
     *
     *         // json处理
     *         $this->post = json_decode(file_get_contents('php://input'), TRUE);
     *
     *         // 普通xml处理
     *         $this->post = simplexml_load_string (
     *             file_get_contents('php://input'),
     *             'SimpleXMLElement',
     *             LIBXML_NOCDATA
     *         );
     *         $this->post = json_decode(json_encode($this->post), TRUE);
     *     }
     * }
     * ```
     * - 其他格式或其他xml可以自行写函数处理
     *
     * @param array|mixed|null $data 参数来源，可以为：$_GET/$_POST/$_REQUEST/自定义
     */
    public function __construct($data = null)
    {
        // 主数据源
        $this->data     = static::genData($data);

        // 备用数据源
        $this->get      = $_GET;
        $this->post     = $_POST;
        $this->request  = $_REQUEST;
        $this->cookie   = $_COOKIE;
    }

    /**
     * 生成请求参数
     * 兼容接收JSON和XML的参数
     * - 此生成过程便于项目根据不同的需要进行定制化参数的限制，如：如只允许接受POST数据，或者只接受GET方式的service参数，以及对称加密后的数据包等
     * - 如果需要定制默认数据源，则可以重载此方法
     *
     * @param null $data 接口参数包
     * @return array
     */
    protected static function genData($data): array
    {
        // 兼容接收JSON的参数
        $postRaw = file_get_contents('php://input');
        if (!empty($postRaw)) {
            // 判断application/xml 或者 application/json
            $content_type = $_SERVER['CONTENT_TYPE'] ?? 'application/json';
            if (stripos($content_type, 'application/xml') !== false) {
                $postRaw = simplexml_load_string($postRaw, 'SimpleXMLElement', LIBXML_NOCDATA);
                $postRaw = json_encode($postRaw);
            }
            $postRawArr = json_decode($postRaw, true);
            if (!empty($postRawArr) && is_array($postRawArr)) {
                $_REQUEST = array_merge($_REQUEST, $postRawArr);
                $_POST = array_merge($_POST, $postRawArr);
            }
        }

        if (!isset($data) || !is_array($data)) {
            return $_REQUEST;
        }

        return $data;
    }

    /**
     * 初始化请求Header头信息
     * @return array|false
     */
    protected static function getAllHeaders()
    {
        if (function_exists('getallheaders')) {
            return getallheaders();
        }

        //对没有getallheaders函数做处理
        $headers = array();
        $copy_server = array(
            'CONTENT_TYPE'   => 'Content-Type',
            'CONTENT_LENGTH' => 'Content-Length',
            'CONTENT_MD5'    => 'Content-Md5',
        );
        foreach ($_SERVER as $name => $value) {
            if (substr($name, 0, 5) === 'HTTP_') {
                $key = substr($name, 5);
                if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) {
                    $key = static::formatHeaderKey($key);
                    $headers[$key] = $value;
                }
            } elseif (isset($copy_server[$name])) {
                $headers[$copy_server[$name]] = $value;
            }
        }
        // 其他
        if (!isset($headers['Authorization'])) {
            if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
                $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
            } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
                $basic_pass = $_SERVER['PHP_AUTH_PW'] ?? '';
                $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
            } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
                $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
            }
        }

        return $headers;
    }

    /**
     * 获取请求的Header参数
     *
     * @param string $key     Header-key值，例如：HTTP_USER_AGENT，或者 USER_AGENT 或：User-Agent
     * @param mixed  $default 默认值
     *
     * @return string|null|mixed
     */
    public function getHeader(string $key, $default = null)
    {
        // 延时加载，提升性能
        if ($this->headers === null) {
            $this->headers = $this->getAllHeaders();
        }

        // 保持一致性，兼容多种格式的KEY输入，提高友好性
        if (0 === stripos($key, 'HTTP_')) {
            $key = substr($key, 5);
        }
        $key = static::formatHeaderKey($key);

        return $this->headers[$key] ?? $default;
    }

    /**
     * 格式化HTTP头部KEY
     * 例如，将HTTP_USER_AGENT转为User-Agent，更贴合浏览器查看的格式
     * @param $key
     * @return string
     */
    public static function formatHeaderKey($key): string
    {
        return str_replace(' ', '-', ucwords(strtolower(str_replace(['-','_'], ' ', $key))));
    }

    /**
     * 直接获取接口参数
     *
     * @param string|int $key     接口参数名字
     * @param mixed  $default 默认值
     *
     * @return mixed
     */
    public function get($key, $default = null)
    {
        return $this->data[$key] ?? $default;
    }

    /**
     * 根据来源标识获取数据集
     * ```
     * |----------|---------------------|
     * | post     | $_POST              |
     * | get      | $_GET               |
     * | cookie   | $_COOKIE            |
     * | server   | $_SERVER            |
     * | request  | $_REQUEST           |
     * | header   | $_SERVER['HTTP_X']  |
     * |----------|---------------------|
     *
     * ```
     * - 当需要添加扩展其他新的数据源时，可重载此方法
     *
     * @param string $source
     * @param mixed $default
     * @return array
     */
    public function &getDataBySource(string $source, $default = null)
    {
        switch (strtoupper($source)) {
            case 'GET':
                return $this->get;
            case 'POST':
                return $this->post;
            case 'COOKIE':
                return $this->cookie;
            case 'HEADER':
                if ($this->headers === null) {
                    $this->headers = $this->getAllHeaders();
                }
                return $this->headers;
            case 'SERVER':
                return $_SERVER;
            case 'REQUEST':
                return $this->request;
            default:
                return $default;
        }
    }

    /**
     * 获取全部接口参数
     * @return array
     */
    public function getAll(): array
    {
        return $this->data;
    }
}
